(define-library (oaxaca finder)

  (export finder-initialized?
          finder-command
          finder-idle?
          finder-wait
          finder-idle!
          finder-configure!
          finder-set-window-center!
          finder-window
          finder-pixel-frequency
          finder-min-frame-frequency
          finder-max-frame-frequency
          finder-frame-frequency
          finder-actual-frame-frequency
          finder-frame-time
          finder-frame
          finder-frames-per-integration
          finder-integration-time
          finder-integration
          finder-integrations-per-exposure
          finder-exposure-time
          finder-z-min
          finder-z-max
          finder-z-mean
          finder-fwhm
          finder-eshm
          finder-spatial-sigma
          finder-temporal-sigma
          finder-search-radius
          finder-set-search-radius!
          finder-refinement-radius
          finder-set-refinement-radius!
          finder-s-vector
          finder-r-vector
          finder-set-r-vector!
          finder-ds-mean-vector
          finder-ds-sigma-vector
          finder-dt-sigma
          finder-i-matrix
          finder-set-i-matrix!
          finder-c-matrix
          finder-set-c-matrix!
          finder-g-vector
          finder-set-g-vector!
          finder-expose!
          finder-dark!
          finder-darks!
          finder-find-spots!
          finder-refine-spots!)

  (import (scheme base)
          (scheme char)
          (scheme time)
          (scheme write))

  (import (prefix (oaxaca detector) detector:)
          (oaxaca file)
          (prefix (oaxaca foreign) foreign:)
          (oaxaca log)
          (oaxaca thread))

  (begin

    (define finder-directory-path (make-path (oaxaca-directory-path) "var" "finder"))
    (make-directory finder-directory-path)

    (define detector-index (foreign:finder-detector-index))

    (define (exposure-fits-file-path)
      (make-path finder-directory-path
                 (string-append "exposure.fits")))

    (define (finder-initialized?)
      (not (zero? (foreign:finder-initialized))))

    (define (check-finder-initialized)
      (unless (finder-initialized?)
        (error "the finder is not initialized."))
      (values))

    (define (finder-idle?)
      (eqv? #\I (foreign:finder-command)))

    (define (check-finder-idle)
      (unless (finder-idle?)
        (error "the finder is not idle."))
      (values))

    (define configuration-name #f)

    (define configuration-alist
      '((full-frame-1  1280 1024  2e6   1)
        (full-frame-2  1280 1024  8e6   2)
        (full-frame-4  1280 1024  8e6   4)
        (full-frame-8  1280 1024 24e6   8)
        (full-frame-16 1280 1024 48e6  16)
        (half-frame-1   640  512  2e6   1)
        (half-frame-2   640  512  2e6   2)
        (half-frame-4   640  512  8e6   4)
        (half-frame-8   640  512  8e6   8)
        (half-frame-16  640  512 24e6  16)
        (half-frame-32  640  512 24e6  32)
        (half-frame-64  640  512 48e6  64)))

    (define (finder-set-window-center! cx cy)
      (detector:set-window-center! detector-index cx cy))
      
    (define (finder-configure! name)      
      (check-finder-initialized)
      (finder-idle!)
      (apply detector:set-configuration!
             detector-index
             finder-directory-path
             name
             (let ((p (assoc name configuration-alist)))
               (unless p
                 (error "invalid configuration name."))
               (cdr p)))
      (set! configuration-name name)
      (finder-command! #\c #\I)
      (finder-wait)
      (values))
        
    (define (finder-window)
      (detector:window detector-index))

    (define (finder-pixel-frequency)
      (detector:pixel-frequency detector-index))

    (define (finder-min-frame-frequency)
      (detector:min-frame-frequency detector-index))

    (define (finder-max-frame-frequency)
      (detector:max-frame-frequency detector-index))

    (define (finder-frame-frequency)
      (detector:frame-frequency detector-index))

    (define (finder-actual-frame-frequency)
      (detector:actual-frame-frequency detector-index))

    (define (finder-frame-time)
      (detector:frame-time detector-index))

    (define (finder-exposure-time)
      (detector:exposure-time detector-index))

    (define (finder-integration-time)
      (detector:integration-time detector-index))

    (define (finder-integrations-per-exposure)
      (detector:integrations-per-exposure detector-index))

    (define (finder-frames-per-integration)
      (detector:frames-per-integration detector-index))

    (define (finder-integration)
      (detector:integration detector-index))

    (define (finder-frame)
      (detector:frame detector-index))

    (define (finder-z-min)
      (detector:z-min detector-index))

    (define (finder-z-max)
      (detector:z-max detector-index))

    (define (finder-z-mean)
      (detector:z-mean detector-index))

    (define (finder-fwhm)
      (detector:fwhm detector-index))

    (define (finder-eshm)
      (detector:eshm detector-index))

    (define (finder-spatial-sigma)
      (detector:spatial-sigma detector-index))

    (define (finder-temporal-sigma)
      (detector:temporal-sigma detector-index))

    (define (finder-s-vector)
      (check-finder-initialized)
      (detector:s-vector detector-index))

    (define (finder-r-vector)
      (check-finder-initialized)
      (detector:r-vector detector-index))

    (define (finder-set-r-vector! x)
      (check-finder-initialized)
      (detector:set-r-vector! detector-index x))

    (define (finder-ds-mean-vector)
      (detector:ds-mean-vector detector-index))

    (define (finder-ds-sigma-vector)
      (detector:ds-sigma-vector detector-index))

    (define (finder-dt-sigma)
      (detector:dt-sigma detector-index))

    (define (finder-idle!)
      (check-finder-initialized)
      (foreign:detector-set-abort! detector-index 1)
      (finder-command! #\i #\I)
      (foreign:detector-set-abort! detector-index 0)
      (values))

    (define (finder-expose! integration-time exposure-time)
      (check-finder-initialized)
      (when (negative? integration-time)
        (error "the integration time must be non-negative."))
      (when (negative? exposure-time)
        (error "the exposure time must be non-negative."))
      (when (and (zero? integration-time) (not (zero? exposure-time)))
        (error "the exposure time must be zero if the integration time is zero."))
      (finder-idle!)
      (foreign:detector-set-integration-time! detector-index (inexact integration-time))
      (foreign:detector-set-exposure-time! detector-index (inexact exposure-time))
      (finder-command! #\e #\E)
      (values))

    (define (finder-dark! integration-time)
      (check-finder-initialized)
      (when (negative? integration-time)
        (error "the integration time must be non-negative."))
      (finder-idle!)
      (foreign:detector-set-integration-time! detector-index (inexact integration-time))
      (foreign:detector-set-exposure-time! detector-index (inexact integration-time))
      (finder-command! #\d #\D)
      (finder-wait)
      (values))

    (define (finder-darks! integration-time)
      (check-finder-initialized)
      (when (negative? integration-time)
        (error "the integration time must be non-negative."))
      (finder-idle!)
             (log-info "saving configuration.")
      (let ((saved-name (or configuration-name 'full-frame-1)))
        (for-each
         (lambda (p)
           (let ((name (car p)))
             (log-info "taking dark for configuration " name ".")
             (finder-configure! name)
             (finder-wait)
             (finder-dark! integration-time)
             (finder-wait)))
         configuration-alist)
             (log-info "restoring configuration " saved-name ".")
        (finder-configure! saved-name)
        (finder-wait)
        (values)))

    (define (finder-find-spots!)
      (check-finder-initialized)
      (finder-idle!)
      (finder-command! #\f #\I)
      (values))

    (define (finder-refine-spots!)
      (check-finder-initialized)
      (finder-idle!)
      (finder-command! #\r #\I)
      (values))

    (define (finder-wait)
      (thread-wait-until! finder-initialized?)
      (thread-wait-until! finder-idle?)
      (values))

    (define (finder-command)
      (foreign:finder-command))

    (define (finder-command! command acknowledgement)
      (check-finder-initialized)
      (foreign:finder-set-command! command)
      (thread-wait-until!
       (lambda ()
         (char=? (foreign:finder-command) acknowledgement)))
      (values))

    (define (finder-search-radius)
      (detector:search-radius detector-index))

    (define (finder-set-search-radius! radius)
      (detector:set-search-radius! detector-index radius))

    (define (finder-refinement-radius)
      (detector:refinement-radius detector-index))

    (define (finder-set-refinement-radius! radius)
      (detector:set-refinement-radius! detector-index radius))

    (define (finder-i-matrix)
      (detector:i-matrix detector-index))

    (define (finder-set-i-matrix! i-matrix)
      (detector:set-i-matrix! detector-index i-matrix))

    (define (finder-c-matrix)
      (detector:c-matrix detector-index))

    (define (finder-set-c-matrix! c-matrix)
      (detector:set-c-matrix! detector-index c-matrix))

    (define (finder-g-vector)
      (detector:g-vector detector-index))

    (define (finder-set-g-vector! gain)
      (detector:set-g-vector! detector-index gain))))
